package com.example.lib_router_compiler.ksp.compiler

import com.example.lib_router_anntation.RouterPath
import com.example.lib_router_anntation.RouterService
import com.example.lib_router_anntation.RouterServiceImpl
import com.google.auto.service.AutoService
import java.io.File
import javax.annotation.processing.*
import javax.lang.model.SourceVersion
import javax.lang.model.element.*
import javax.tools.Diagnostic


@AutoService(Processor::class)
class Compiler : AbstractProcessor() {
    private var packageName = ""
    private var className = ""

    private val routerPathMap = mutableMapOf<String, String>()
    private val routerServiceMap = mutableMapOf<String, String>()
    private val routerServiceImplMap = mutableMapOf<String, RouterServiceImplInfo>()
    private var isProcess = false


    override fun init(processingEnv: ProcessingEnvironment) {
        super.init(processingEnv)
        val kaptKotlinGeneratedDir = processingEnv.options["kapt.kotlin.generated"]
        if (kaptKotlinGeneratedDir != null) {
            className = getModuleName(kaptKotlinGeneratedDir)
            packageName = DEFAULT_PACKAGE
        }
    }

    override fun getSupportedAnnotationTypes(): Set<String> {
        val types = LinkedHashSet<String>()
        types.add(RouterPath::class.java.canonicalName)
        types.add(RouterService::class.java.canonicalName)
        types.add(RouterServiceImpl::class.java.canonicalName)
        return types
    }

    override fun getSupportedSourceVersion(): SourceVersion {
        return SourceVersion.latestSupported()
    }

    override fun process(
        annotations: MutableSet<out TypeElement>,
        roundEnv: RoundEnvironment): Boolean {
        if (!isProcess) {
            isProcess = true
            val routerPathElements = roundEnv.getElementsAnnotatedWith(RouterPath::class.java)
            routerPathElements.forEach {
                if (it.kind != ElementKind.CLASS) {
                    "@RouterPath must be annotated at Class!".loge()
                } else {
                    val annotation = it.getAnnotation(RouterPath::class.java)
                    val scheme = annotation.scheme
                    routerPathMap[scheme] = it.toString()
                }
            }

            val routerServiceElements = roundEnv.getElementsAnnotatedWith(RouterService::class.java)
            routerServiceElements.forEach {
                if (it.kind != ElementKind.INTERFACE) {
                    "@RouterService must be annotated at Interface!".loge()
                } else {
                    val annotation = it.getAnnotation(RouterService::class.java)
                    val name = it.simpleName
                    val identity = annotation.identity
                    val realKey = identity.ifEmpty { name }.toString()
                    routerServiceMap[realKey] = it.toString()
                }
            }

            val routerServiceImplElements =
                roundEnv.getElementsAnnotatedWith(RouterServiceImpl::class.java)
            routerServiceImplElements.forEach {
                if (it.kind != ElementKind.CLASS) {
                    "@RouterServiceImpl must be annotated at Class!".loge()
                } else {
                    val annotation = it.getAnnotation(RouterServiceImpl::class.java)
                    val singleton = annotation.singleton
                    val name = it.simpleName
                    val identity = annotation.identity
                    if (identity.isEmpty() && !name.endsWith("Impl")) {
                        "@RouterServiceImpl class name must end with Impl!".loge()
                    }
                    val realKey = identity.ifEmpty {
                        val index = name.lastIndexOf("Impl")
                        name.substring(0, index)
                    }.toString()
                    routerServiceImplMap[realKey] = RouterServiceImplInfo(it.toString(), singleton)
                }
            }

            val kaptKotlinGeneratedDir = processingEnv.options["kapt.kotlin.generated"]
            val generator = Generator(
                packageName,
                className,
                routerPathMap,
                routerServiceMap,
                routerServiceImplMap)
            if (kaptKotlinGeneratedDir != null) {
                generator.generate().writeTo(File(kaptKotlinGeneratedDir))
            }
        }

        return isProcess
    }

    private fun String.logn() {
        processingEnv.messager.printMessage(Diagnostic.Kind.NOTE, this)
    }

    private fun String.logw() {
        processingEnv.messager.printMessage(Diagnostic.Kind.WARNING, this)
    }

    private fun String.loge() {
        processingEnv.messager.printMessage(Diagnostic.Kind.ERROR, this)
    }
}